#include "iic.h"
/*
	1、i2c为两线串行半双工通信总线,分别有scl时钟信号线、sda数据传输线，主要用于近距离、低速的芯片之间的通信，
	2、采用多主机多从,每个设备都有唯一地址（ID号）,同一时刻只能有一个主机,当有多个主机时,时钟线低电平优先。
	3、具有应答机制，时钟线低电平时数据有效，通信速率有 标准（100kbit/s）、快速（400kbit/s）、高速（3.4Mbit/s）
*/

//配置i2c的GPIO引脚参数
void iicInit(void)
{
	GPIO_InitTypeDef GPIO_InitStruct;
	RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOB, ENABLE);
	
	GPIO_InitStruct.GPIO_Mode = GPIO_Mode_OUT;
	GPIO_InitStruct.GPIO_OType = GPIO_OType_PP;
	GPIO_InitStruct.GPIO_Pin = GPIO_Pin_6|GPIO_Pin_7;
	GPIO_InitStruct.GPIO_PuPd = GPIO_PuPd_NOPULL;
	GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIOB, & GPIO_InitStruct);
}

//配置sda的输入模式
void sdaIn(void)
{
	GPIO_InitTypeDef GPIO_InitStruct;
	GPIO_InitStruct.GPIO_Mode = GPIO_Mode_IN;
	GPIO_InitStruct.GPIO_Pin = GPIO_Pin_7;
	GPIO_Init(GPIOB, & GPIO_InitStruct);
}

//配置sda的输出模式
void sdaOut(void)
{
	GPIO_InitTypeDef GPIO_InitStruct;
	GPIO_InitStruct.GPIO_Mode = GPIO_Mode_OUT;
	GPIO_InitStruct.GPIO_Pin = GPIO_Pin_7;
	GPIO_Init(GPIOB, & GPIO_InitStruct);
}

//i2c通信开始信号
void iicStart(void)
{
	SCL_L;
	sdaOut();
	SDA_H;
	SCL_H;
	vTaskDelay(5);
	SDA_L;
	vTaskDelay(5);
	SCL_L;
}

//i2c通信停止信号
void iicStop(void)
{
	SCL_L;
	sdaOut();
	SDA_L;
	SCL_H;
	vTaskDelay(5);
	SDA_H;
	vTaskDelay(5);
	SCL_H;
}

//i2c发送应答信号
void iicSendACK(void)
{
	SCL_L;
	sdaOut();
	SDA_L;
	SCL_H;
	vTaskDelay(5);
	SCL_L;
}

//i2c发送非应答信号
void iicSendNACK(void)
{
	SCL_L;
	sdaOut();
	SDA_H;
	SCL_H;
	vTaskDelay(5);
	SCL_L;
}

//i2c等待应答信号
uint8_t iicWaitACK(void)
{
	uint16_t timeout;
	SCL_L;
	sdaIn();
	SCL_H;
	
	while(SDA_READ)//GPIO_ReadInputDataBit(GPIOB,GPIO_Pin_7)，当电平为1时,说明为非应答信号,循环等待
	{		
		timeout++;
		if(timeout > 300)//超时未检测到应答信号，则结束通信
		{
			iicStop();
			return NACK;
		}
	}
	return ACK;
}

//i2c发送数据（字节）
void iicSendByte(uint8_t data)
{
	uint8_t i;
	SCL_L;
	sdaOut();
	//串行发送,高位先行
	for(i = 0; i < 8; i++)
	{
		if(data & 0x80)//通过&0x80,判断最高位数据,每次循环数据左移,完成按位发送
		{
			SDA_H;//1设置sda为高电平
		}
		else
		{
			SDA_L;//0设置sda为低电平
		}
		SCL_H;
		vTaskDelay(5);//延时保证数据在有效时钟内稳定不变
		SCL_L;
		vTaskDelay(5);//延时为传输下一位数据做准备
		data  <<= 1;//左移一位开始传输下一位数据
	}
}

//i2c接收数据（字节）
uint8_t iicReceiveByte(uint8_t ack_flag)
{
	uint8_t i, read_data = 0;
	SCL_L;
	sdaIn();
	//串行接收，高位先行，循环完成接收一个字节
	for(i = 0; i < 8; i++)
	{
		read_data = read_data << 1;//左移一位开始接收下一位数据，同时放在循环开始避免最后一位丢失
		SCL_H;
		if(SDA_READ)//当电平为1时,说明该数据位为1
		{
			read_data = read_data | 0x01;//通过|0x01,将最低位置1,每次循环,数据左移,完成按位接收
		}
		vTaskDelay(5);
		SCL_L;
		vTaskDelay(5);
	}
	if(ack_flag == ACK)
	{
		iicSendACK();
	}
	else
	{
		iicSendNACK();
	}
	return read_data;
}

